using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate;
using NHibernate.Linq;

namespace HashFoo.Nhib
{
    /// <summary>
    /// Utility classes for an nhibernate session.  Should be used as extensions.
    /// </summary>
    public static class SessionUtil
    {
        /// <summary>
        /// Ensures that a DbEnum record exists.
        /// If the DbEnum has to be created, the entity is saved, but the session is not flushed.
        /// </summary>
        /// <typeparam name="T">The type of DbEnum</typeparam>
        /// <param name="session">The session to use for persistence</param>
        /// <param name="dbEnum">The DbEnum to ensure</param>
        /// <returns>The ensured DbEnum</returns>
        public static T EnsureDbEnum<T>(this ISession session, T dbEnum)
            where T : DbEnum
        {
            var ensuredEnum = session.Query<T>().FirstOrDefault(e => e.InternalName == dbEnum.InternalName);
            if (ensuredEnum == null)
            {
                ensuredEnum = dbEnum;
                session.Save(ensuredEnum);
            }

            return ensuredEnum;
        }

        public static T Ensure<T>(this ISession session, T entity, Expression<Func<T, bool>> existencePredicate)
            where T : class 
        {
            var ensuredEntity = session.Query<T>().FirstOrDefault(existencePredicate);

            if (ensuredEntity == null)
            {
                session.SaveOrUpdate(entity);
                return entity;
            }

            return ensuredEntity;
        }

        /// <summary>
        /// Gets a single result from a predicate query.  Will optionally throw an exception if none is found.  Otherwise, returns default (null).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="session"></param>
        /// <param name="predicate"></param>
        /// <param name="throwIfNoneReturned"></param>
        /// <returns>The first result, or default value (null)</returns>
        public static T GetSingle<T>(this ISession session, Expression<Func<T, bool>> predicate, bool throwIfNoneReturned = false)
            where T : class 
        {
            var entity = session.Query<T>().FirstOrDefault(predicate);
            if (throwIfNoneReturned && entity == null) throw new InvalidOperationException("No entity was found");

            return entity;
        }
    }
}